<body></body>
<script>
  let activeEffect = null //用于收集当前激活的副作用
  let bucket = new WeakMap() //存储副作用

  //原始数据
  const data = {
    ok: false,
    text: 'hello wrold'
  }

  // 对数据进行代理：
  // 1、get（）返回 本身数据 和 更新储存对应副作用
  // 2、set（）修改值的本身 和 运行对应新副作用
  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newVal) {
      target[key] = newVal
      trigger(target, key)
    }
  })

  function track(target, key) {
    if (!activeEffect) return target[key] //没有通过effect调用只是数据的读取，不产生副作用，也就触发视图的更新
    let depMap = bucket.get(target) //先找到这个对象 >然后找到 对象下对应的key所绑定读取到副作用，就是个Set集合
    if (!depMap) {
      bucket.set(target, (depMap = new Map())) //没有的话就建一个 属于该对象的 map
    }
    let deps = depMap.get(key) //找到了 当前属性 key的绑定副作用
    if (!deps) {
      depMap.set(key, (deps = new Set())) //没有也是建一个属于改 key的Set副作用集合
    }
    deps.add(activeEffect) //往这个属性加上副作用
    activeEffect.deps.push(deps)
    // console.log(activeEffect.deps) // 打印测试
  }

  function trigger(target, key) {
    let depMap = bucket.get(target)
    if (!depMap) return //说明副作用函数里面根本没有被读取，所以不会产生副作用，这里直接返回就可以了
    let effects = depMap.get(key)
    // effects.forEach((fn) => fn())
    effectsToRun = new Set(effects)
    effectsToRun && effectsToRun.forEach(effectFn => effectFn())
  }

  function effect(fn) {
    // 副作用重叠了，执行的时候，出现运行多次副作用，比如 obj.ok ? obj.text : 'not' 绑定了同一个副作用
    // 如果 ok是false只会存到一次text的副作用，但是ok开始是true，改成ok是false之后
    function effectFn() {
      cleanup(effectFn) //清楚这里的effectFn存的deps，
      activeEffect = effectFn // 清理掉的话 activeEffect就只是一个函数了
      fn()
    }
    effectFn.deps = [] //初始化deps
    effectFn()
  }
  function cleanup(effectFn) {
    for (let deps of effectFn.deps) {
      deps.delete(effectFn)
    }
    effectFn.deps.length = 0
  }
  effect(() => {
    console.log('更新页面测试')
    document.body.innerText = obj.ok ? obj.text : 'not' //这是一个短路的运算，ok是false，不会触发text的get
  })

  // 在 obj.ok ? obj.text 情况触发了两次effectfn是对的，如果ok是false只会触发一次的副作用更新
  // 在更新的时候出现了依赖没有清理的话，第二次就出现问题了，即使ok被改过之后，text的副作用还存在，
  // 但是text明明不需要副作用了更新视图了，出现了再次调用副作用的问题，所以每一次读取的对象对的时候

  // 总结：
  // 1、在 effect 注册activeEffect，顺便新建activeEffect的deps数组
  // 2、ky遇到 set读取的时候，在activeEffect数组副作用进去
  // 3、key被修改，执行 key对应的副作用函
  // 4、key读取，会把副作用加回去（Set 是一种集合数据结构，它会自动去重。）
  // 修改之前的 activeEffect，用来跟踪当前正在执行的副作用函数
  // activeEffect.deps.push(deps)
  setTimeout(() => {
    //触发第二次
    obj.ok = false
  }, 1000)

  setTimeout(() => {
    obj.text = '123'
  }, 1300)
  setTimeout(() => {
    obj.text = '123'
  }, 1650)
  setTimeout(() => {
    obj.text = '123'
  }, 2000)

  setTimeout(() => {
    // 触发第三次
    obj.ok = true
  }, 3000)
  setTimeout(() => {
    // 触发第四次 副作用
    obj.text = '123'
  }, 4000)
  // 打印 4次
</script>
